package br.senai.sc.controller.business;

import java.math.BigDecimal;

import br.senai.sc.controller.exception.BusinessException;

/**
 * Contém os cálculos de IQA para o parâmetro Turbidez.
 * 
 * @author Tiago
 */
public final class CalculoTurbidezBusiness {

	/**
	 * Peso fixo utilizado para o cálculo da qualidade da Turbidez (qi^w)
	 */
	private static final double PESO = 0.08;

	/**
	 * Define o intervalo para o cálculo. De mais que 0 a 100.
	 */
	public static final int[] PRIMEIRO_INTERVALO = { 0, 100 };

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * retorno do calculo de cada intervalo de valor.
	 */
	public static final int ESCALA = 10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * cálculo de exponenciais.
	 */
	private static final int ESCALA_EXP = 20;

	private CalculoTurbidezBusiness() {
	}

	/**
	 * Utilizado para fazer o cálculo da Turbidez caso o valor informado é maior
	 * ou igual a 0 e menor ou igual a 100.
	 * 
	 * Cálculo realizado: -5,14705882393731E-10*B13^6 +
	 * 1,17552790352554E-07*B13^5 - 6,89196832492911E-06*B13^4 -
	 * 0,000260476655824959*B13^3 + 0,0461429247295655*B13^2 -
	 * 2,52553304584762*B13 + 96,9874537419573;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal primeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		if ((valor.compareTo(BigDecimal.ZERO) < PRIMEIRO_INTERVALO[0])
				|| (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp6 = new BigDecimal(Math.pow(valor.doubleValue(), 6))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp5 = new BigDecimal(Math.pow(valor.doubleValue(), 5))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp4 = new BigDecimal(Math.pow(valor.doubleValue(), 4))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp3 = new BigDecimal(Math.pow(valor.doubleValue(), 3))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// -5,14705882393731E-10*B13^6 + 1,17552790352554E-07*B13^5 -
		// 6,89196832492911E-06*B13^4 - 0,000260476655824959*B13^3 +
		// 0,0461429247295655*B13^2 - 2,52553304584762*B13 + 96,9874537419573;
		BigDecimal retorno = new BigDecimal("5.14705882393731E-10")
				.multiply(valorExp6)
				.negate()
				.add(new BigDecimal("1.17552790352554E-07").multiply(valorExp5))
				.subtract(
						new BigDecimal("6.89196832492911E-06")
								.multiply(valorExp4))
				.subtract(
						new BigDecimal("0.000260476655824959")
								.multiply(valorExp3))
				.add(new BigDecimal("0.0461429247295655").multiply(valorExp2))
				.subtract(new BigDecimal("2.52553304584762").multiply(valor))
				.add(new BigDecimal("96.9874537419573"));

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

/**
	 * Calcula o IQA da Turbidez informada.
	 * Cálculo realizado:
	 * 	Se [valor] < que 0
	 * 		Lança exceção
	 *  Se [valor] <= 100 
	 *  	@see {@link CalculoTurbidezBusiness#primeiroIntervalo(double)
	 *  Se [valor] > 100
	 *  	IQA = 5
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException Lançada caso o valor informado seja inválido (Ex: menor que 0).
	 */
	public static BigDecimal calculaIQA(BigDecimal valor)
			throws BusinessException {

		// =SE(B13="";"NM";SE(B13<0;"ERR";
		// se o valor for menor que 0 está incorreto
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) < 0) {
			throw new BusinessException();
		}

		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) <= 0) {
			return primeiroIntervalo(valor);
		}

		// se o valor é maior do que 100 sempre é 5
		final int retornoMaiorQueUltimoLimite = 5;
		return new BigDecimal(retornoMaiorQueUltimoLimite);
	}

	/**
	 * Calcula a média de qualidade (qi^w) do DBO.
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido.
	 */
	protected static BigDecimal mediaQualidade(BigDecimal valor)
			throws BusinessException {
		return new BigDecimal(Math.pow(calculaIQA(valor).doubleValue(), PESO));
	}

}
